package com.xyht.medicine_management_system.auth.aop;

import lombok.extern.log4j.Log4j2;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;


@Component
@Aspect
@ComponentScan
@EnableAspectJAutoProxy
@Log4j2
public class LogAopUtil {
    @Autowired
    private HttpServletRequest request;

    private Map<String,Object> paramMap = new HashMap<>();
    private Logger logger = LoggerFactory.getLogger(LogAopUtil.class);

    /**
     * 打印类method的名称以及参数
     *
     * @param point 切面
     */
    public void printMethodParams(JoinPoint point) {
        if (point == null) {
            return;
        }

        String className = point.getTarget().getClass().getName();
        String simpleClassName = point.getTarget().getClass().getSimpleName();
        String methodName = point.getSignature().getName();
        // 获取方法的参数值数组。
        Object[] method_args = point.getArgs();
        try {
            // 获取方法参数名称
            String[] paramNames = getFieldsName(className, methodName);
            // 打印
            logParam(paramNames, method_args, className, methodName, simpleClassName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用javassist来获取方法参数名称
     *
     * @param className  类名
     * @param methodName 方法名
     * @return
     * @throws Exception
     */
    private String[] getFieldsName(String className, String methodName) throws Exception {
        Class<?> clazz = Class.forName(className);
        String clazz_name = clazz.getName();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(clazz);
        pool.insertClassPath(classPath);

        CtClass ctClass = pool.get(clazz_name);
        CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            return null;
        }
        String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramsArgsName.length; i++) {
            paramsArgsName[i] = attr.variableName(i + pos);
        }
        return paramsArgsName;
    }


    /**
     * 判断是否为基本类型：包括String
     *
     * @param clazz clazz
     * @return true：是;     false：不是
     */
    private boolean isPrimite(Class<?> clazz) {
        if (clazz.isPrimitive() || clazz == String.class) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 日志输出
     *
     * @param paramsArgsName
     * @param paramsArgsValue
     * @param className
     * @param methodName
     * @param simpleClassName
     */
    private void logParam(String[] paramsArgsName, Object[] paramsArgsValue, String className, String methodName, String simpleClassName) {

        StringBuilder sb = new StringBuilder("\nSpringBoot controller report -------- ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append(" ----------------------\n");
        sb.append("Controller  : ").append(className).append(".(").append(simpleClassName).append(".java:1)");
        sb.append("\nMethod      : ").append(methodName).append("\n");
        if (ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)) {
            sb.append("ParamContent: no param  \n");
            sb.append("--------------------------------------------------------------------------------\n");
            logger.info(sb.toString());
            return;
        }
        if (paramsArgsName.length == 0){
            return;
        }
        for (int i = 0; i < paramsArgsName.length; i++) {
            //参数名
            String name = paramsArgsName[i];
            //参数值
            if (i>=paramsArgsValue.length){
                continue;
            }
            Object value = paramsArgsValue[i];
            sb.append("ParamContent: " + name + " = ");
            if (value == null) continue;
            paramMap.put(name,value);   //赋值一个全局的请求方法的参数存储变量
            if (isPrimite(value.getClass())) {
                sb.append(value + "  \n");
            } else {
                sb.append(value.toString() + "  \n");
            }
        }
        sb.append("--------------------------------------------------------------------------------\n");
        logger.info(sb.toString());
    }

    public LogAopUtil(HttpServletRequest request) {
        this.request = request;
    }

    @Before("execution(public * com.xyht..controller..*(..))")
    public void before(JoinPoint point) {
        this.printMethodParams(point);
    }

    @AfterReturning(returning = "returnOb", pointcut = "execution(public * com.xyht..controller..*(..))")
    public void doAfterReturning(JoinPoint joinPoint, Object returnOb) {

    }

    @AfterThrowing(pointcut = "execution(public * com.xyht..controller..*(..))", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        log.debug("连接点方法为：" + methodName + ",参数为：" + args + ",异常为：" + ex);
    }

//    @Pointcut("@annotation(com.xyht.smart_town_pro.auth.my_annotation.Permissions)")
//    public void permissionsPoint() {
//    }

//    @Around("permissionsPoint()")
//    public Object hasPermissions(ProceedingJoinPoint joinPoint) throws Throwable {
//        //方法签名
//        MethodSignature methodSignature=(MethodSignature) joinPoint.getSignature();
//        //获取方法
//        Method aimMethod= methodSignature.getMethod();
//        //获取 @Permissions 的值
//        String permissions=aimMethod.getAnnotation(Permissions.class).value();
//        //权限集合
//        List<String> permissionsList=new ArrayList<>();
//        permissionsList.add("/user/test");
//        //返回数据
//        Object proceed=null;
//        //如果权限集合包含 @Permissions 的值
//        if(permissionsList.contains(permissions)){
//            //允许通过，表调用的方法可以往下运行
//            proceed=joinPoint.proceed();
//        }else {
//            proceed= CommonCodeEnum.FAIL;
//        }
//        return proceed;
//    }



}
